/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package control;

import java.util.LinkedList;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.Processo;
import model.Memoria;
import model.Processador;
import view.JanelaEscalonador;
import view.ProcessadoresViewFrame;

/**
 *
 * @author
 * Abraão de Santana
 * Jorge Thiago
 * Leandro C. Arantes
 */
public class SistemaOperacional {

    private static LinkedList<Processo> FE = new LinkedList<Processo>();  //Fila de entrada dos processos;
    private static LinkedList<Processo> FTR = new LinkedList<Processo>(); //Fila de processos de tempo real;
    private static LinkedList<Processo> FU = new LinkedList<Processo>();  //Fila de processos de usuário;
    private static LinkedList<Processo> PROCESSOS_SUSPENSOS = new LinkedList<Processo>();
    private static LinkedList<Processo> PROCESSOS_BLOQUEADOS = new LinkedList<Processo>();

    private static boolean run = false;
    private static int systemTime = 0;

    private static int printersAvailable = 2;
    private static int scannerAvailable = 1;
    private static int modemAvailable = 1;
    private static int cdDriversAvailable = 2;

    private Memoria mem1 = new Memoria(1024);
    private Memoria mem2 = new Memoria(1024);

    private ProcessadoresViewFrame frame;
    
    //private JanelaEscalonador frame2;

    public SistemaOperacional(ProcessadoresViewFrame frame) {
        this.frame = frame;
    }

    public void init()
    {
        DespachanteFCFS fcfsDispatcher = new DespachanteFCFS(FTR);

        Processador processador1 = new Processador(mem1, frame, 1);
        Processador processador2 = new Processador(mem1, frame, 2);
        Processador processador3 = new Processador(mem2, frame, 3);
        Processador processador4 = new Processador(mem2, frame, 4);

        Vector<Processador> processadores = new Vector<Processador>();
        processadores.add(processador1);
        processadores.add(processador2);
        processadores.add(processador3);
        processadores.add(processador4);
        
        DespachanteFeedback fbDispatcher = new DespachanteFeedback(FU, processadores);

        run = true;
        systemTime = 0;
        while ( run )
        {
            systemTime++;
            //frame2.setTemporizador();
            CriadorDeProcessos.verificaProcessosNovos(systemTime);

            if ( systemTime % 10 == 0 )
            {
                verificaProcessosBloqueados();
                verificaFE();
                verificaProcessosSuspensos();
            }

            updateTimeAtProcessors(processadores);

            fbDispatcher.update(false);

            for ( Processador processor : processadores )
            {
                if ( !processor.isExecutando() )
                {
                    if ( FTR.isEmpty() )
                    {
                        fbDispatcher.activate(processor);
                    }
                    else
                    {
                        fcfsDispatcher.activate(processor);
                    }
                }
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                Logger.getLogger(SistemaOperacional.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public static void submitProcess( Processo process )
    {
        process.setEstado(Processo.NOVO);
        FE.add(process);
    }

    private Memoria getMemoryWithMoreAvailableSpace() {
        if ( mem1.getEspacoVazio() > mem2.getEspacoVazio() )
        {
            return mem1;
        }
        else
        {
            return mem2;
        }
    }

    private void verificaFE()
    {
        while ( !FE.isEmpty() )
        {
            Processo process = FE.poll();
            Memoria mem = getMemoryWithMoreAvailableSpace();
            boolean added = mem.addProcesso(process);

            if ( !added )
            {
                if ( process.getPrioridade() == 0 )
                {
                    Processo processSuspended = mem.exchangeProcesses( process );
                    if ( processSuspended != null )
                    {
                        PROCESSOS_SUSPENSOS.add(processSuspended);
                        processSuspended.setEstado(Processo.SUSPENSO);
                        FTR.add(process);
                        process.setEstado(Processo.PRONTO);
                    }
                }
                else
                {
                    PROCESSOS_SUSPENSOS.add(process);
                    process.setEstado(Processo.SUSPENSO);
                }
            }
            else
            {
                if ( process.getPrioridade() == 0 )
                {
                    FTR.add(process);
                    process.setEstado(Processo.PRONTO);
                }
                else
                {
                    if ( verificarRecursosRequisitados( process ) )
                    {
                        FU.add(process);
                        process.setEstado(Processo.PRONTO);
                    }
                    else
                    {
                        PROCESSOS_BLOQUEADOS.add(process);
                        process.setEstado(Processo.BLOQUEADO);
                    }
                }
            }
        }
    }

    private void updateTimeAtProcessors(Vector<Processador> processors)
    {
        for ( Processador processor : processors )
        {
            processor.increaseTime();
        }
    }

    private static boolean verificarRecursosRequisitados(Processo process)
    {
        if ( (process.getCds() > cdDriversAvailable) || (process.getModem() > modemAvailable) ||
                (process.getImpressoras() > printersAvailable) || (process.getScanner() > scannerAvailable) )
        {
            return false;
        }
        else
        {
            cdDriversAvailable -= process.getCds();
            modemAvailable -= process.getModem();
            printersAvailable -= process.getImpressoras();
            scannerAvailable -= process.getScanner();
            return true;
        }
    }

    public static int getSystemTime() {
        return systemTime;
    }

    public static void freeResources(Processo process) {
        printersAvailable += process.getImpressoras();
        scannerAvailable += process.getScanner();
        modemAvailable += process.getModem();
        cdDriversAvailable += process.getCds();
    }

    private void verificaProcessosBloqueados() {
        Processo process = PROCESSOS_BLOQUEADOS.peek();
        if ( process != null && verificarRecursosRequisitados( process ) )
        {
            FU.add(process);
            process.setEstado(Processo.PRONTO);
            PROCESSOS_BLOQUEADOS.remove(process);
        }
    }

    private void verificaProcessosSuspensos() {
        Processo processo = PROCESSOS_SUSPENSOS.peek();
        if ( processo != null )
        {
            Memoria mem = getMemoryWithMoreAvailableSpace();
            boolean added = mem.addProcesso(processo);

            if ( added )
            {
                if ( verificarRecursosRequisitados( processo ) )
                {
                    FU.add(processo);
                    processo.setEstado(Processo.PRONTO);
                }
                else
                {
                    PROCESSOS_BLOQUEADOS.add(processo);
                    processo.setEstado(Processo.BLOQUEADO);
                }
                PROCESSOS_SUSPENSOS.remove(processo);
            }
        }
    }
}
